#if !defined(__SONY_ICX692_H_)
#define __SONY_ICX692_H_

#include <stdio.h>
#include <unistd.h>		// usleep
#include <string.h>

#include "hi_comm_sns.h"
#include "hi_sns_ctrl.h"
#include "mpi_isp.h"

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* End of #ifdef __cplusplus */


#define EXPOSURE_ADDR (0x208) //2:chip_id, 0C: reg addr.

#define PGC_ADDR (0x21E)
#define VMAX_ADDR (0x205)
/****************************************************************************
 * local variables															*
 ****************************************************************************/

static cmos_inttime_t cmos_inttime;
static cmos_gains_t cmos_gains;
HI_U8 gu8SensorMode = 0;

static cmos_isp_default_t st_coms_isp_default =
{
    // color correction matrix
    {
        5000,
    	{	0x1b7,  0x8079, 0x803d,
    		0x806d, 0x01f2, 0x8084,
    		0x800a, 0x80b9, 0x01c4
    	},
    	3200,
        {
            0x01e7, 0x80cd, 0x801a,
            0x808f, 0x01d3, 0x8044,
            0x001b, 0x813b, 0x021f
        },
        2600,
        {
            0x020a, 0x80ed, 0x801d,
            0x806e, 0x0196, 0x8028,
            0x0015, 0x820f, 0x02f9
        }
    },

	// black level for R, Gr, Gb, B channels
	{0xf0,0xf0,0xf0,0xf0},

    // calibration reference color temperature
    5000,

    //WB gain at 5000K, must keep consistent with calibration color temperature
	{0x1c5, 0x100, 0x100, 0x1ec},

    // WB curve parameters, must keep consistent with reference color temperature.
	{22, 141, -84, 186260, 0x80, -134565},

	// hist_thresh
	{0xd,0x28,0x60,0x80},

	0x00,	// iridix_balck
	0x0,	// rggb

	// gain
	0x10,	0x8,

	// iridix space, intensity, slope_max, white level
	0x02,	0x08,	0x80, 	0x8ff,

	0x1, 	// balance_fe
	0x80,	// ae compensation
	0x20, 	// sinter threshold

	0x1,        //0: use default profile table; 1: use calibrated profile lut, the setting for nr0 and nr1 must be correct.
	0,
	1528
};


static cmos_isp_agc_table_t st_isp_agc_table =
{
//sharpen_alt_d
{0x88,0x85,0x80,0x7b,0x78,0x72,0x70,0x60},

//sharpen_alt_ud
{0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x70},

//snr_thresh
{0x06,0x8,0xb,0x16,0x22,0x28,0x2e,0x35},

//demosaic_lum_thresh
{0x60,0x60,0x80,0x80,0x80,0x80,0x80,0x80},

//demosaic_np_offset
{0x0,0xa,0x12,0x1a,0x20,0x28,0x30,0x30},

//ge_strength
{0x55,0x55,0x55,0x55,0x55,0x55,0x37,0x37}

};

static cmos_isp_noise_table_t st_isp_noise_table =
{
  //nosie_profile_weight_lut

    {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0c,0x11,0x14,0x17,0x19,0x1b,0x1c,0x1e,0x1f,
    0x20,0x21,0x22,0x23,0x24,0x24,0x25,0x26,0x26,0x27,0x28,0x28,0x29,0x29,0x2a,0x2a,0x2a,
    0x2b,0x2b,0x2c,0x2c,0x2c,0x2d,0x2d,0x2d,0x2e,0x2e,0x2e,0x2f,0x2f,0x2f,0x30,0x30,0x30,
    0x30,0x31,0x31,0x31,0x31,0x32,0x32,0x32,0x32,0x32,0x33,0x33,0x33,0x33,0x34,0x34,0x34,
    0x34,0x34,0x34,0x35,0x35,0x35,0x35,0x35,0x35,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x37,
    0x37,0x37,0x37,0x37,0x37,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x39,0x39,0x39,0x39,0x39,
    0x39,0x39,0x39,0x39,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3b,0x3b,0x3b,0x3b,
    0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3c,0x3c,0x3c
    },
  
    {
    0x04,0x0c,0x11,0x14,0x17,0x19,0x1b,0x1c,0x1e,0x1f,
    0x20,0x21,0x22,0x23,0x24,0x24,0x25,0x26,0x26,0x27,0x28,0x28,0x29,0x29,0x2a,0x2a,0x2a,
    0x2b,0x2b,0x2c,0x2c,0x2c,0x2d,0x2d,0x2d,0x2e,0x2e,0x2e,0x2f,0x2f,0x2f,0x30,0x30,0x30,
    0x30,0x31,0x31,0x31,0x31,0x32,0x32,0x32,0x32,0x32,0x33,0x33,0x33,0x33,0x34,0x34,0x34,
    0x34,0x34,0x34,0x35,0x35,0x35,0x35,0x35,0x35,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x37,
    0x37,0x37,0x37,0x37,0x37,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x39,0x39,0x39,0x39,0x39,
    0x39,0x39,0x39,0x39,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3b,0x3b,0x3b,0x3b,
    0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c
    },
    
};

/*
 * This function initialises an instance of cmos_inttime_t.
 */
static __inline cmos_inttime_const_ptr_t cmos_inttime_initialize()
{
	cmos_inttime.full_lines_std = 1125;
	cmos_inttime.full_lines_std_30fps = 1125;
	cmos_inttime.full_lines = 1125;
	cmos_inttime.full_lines_limit = 65535;
	cmos_inttime.max_lines_target = 1123;
	cmos_inttime.min_lines_target = 2;
	cmos_inttime.vblanking_lines = 1125;

	cmos_inttime.exposure_ashort = 0;

	cmos_inttime.lines_per_500ms = 16874; // 500ms / 29.63us = 16874
	cmos_inttime.flicker_freq = 0;//60*256;//50*256;

	return &cmos_inttime;
}

/*
 * This function applies the new integration time to the ISP registers.
 */
static __inline void cmos_inttime_update(cmos_inttime_ptr_t p_inttime) 
{
    HI_U16 exp_time;

    exp_time = p_inttime->full_lines - p_inttime->exposure_ashort;


    sensor_write_register(EXPOSURE_ADDR, exp_time & 0xFF);
    sensor_write_register(EXPOSURE_ADDR + 1, (exp_time & 0xFF00) >> 8);
}

/*
 * This function applies the new vert blanking porch to the ISP registers.
 */
static __inline void cmos_vblanking_update(cmos_inttime_const_ptr_t p_inttime)
{
	HI_U16 vmax = p_inttime->full_lines;
 //         printf("vmax=%d",vmax);
	sensor_write_register(VMAX_ADDR, (vmax&0x00ff));
	sensor_write_register(VMAX_ADDR+1, ((vmax&0xff00) >> 8));
	
	return;
}

static __inline HI_U16 vblanking_calculate(
		cmos_inttime_ptr_t p_inttime)
{
	if (p_inttime->exposure_ashort >= p_inttime->full_lines - 3)
	{
		p_inttime->exposure_ashort = p_inttime->full_lines - 3;
	}

	p_inttime->vblanking_lines = p_inttime->full_lines - p_inttime->full_lines_std;
    
//    printf("vblanking_lines=%d",p_inttime->vblanking_lines);

	return p_inttime->exposure_ashort;
}


/* Set fps base */
static __inline void cmos_fps_set(
		cmos_inttime_ptr_t p_inttime,
		const HI_U8 fps
		)
{
	switch(fps)
	{
		case 30:
			// Change the frame rate via changing the vertical blanking
			p_inttime->full_lines_std = 1125;
			sensor_write_register(VMAX_ADDR, 0x65);
			sensor_write_register(VMAX_ADDR+1, 0x04);
			p_inttime->lines_per_500ms = 1125 * 30 / 2;
		break;
		
		case 25:
			// Change the frame rate via changing the vertical blanking
			p_inttime->full_lines_std = 1350;
			sensor_write_register(VMAX_ADDR, 0x46);
			sensor_write_register(VMAX_ADDR+1, 0x05);
			p_inttime->lines_per_500ms = 1350 * 25 / 2;
		break;
		
		default:
		break;
	}
}

/*
 * This function initialises an instance of cmos_gains_t.
 */
static __inline cmos_gains_ptr_t cmos_gains_initialize()
{
	cmos_gains.again_shift = 4;
	cmos_gains.dgain_shift = 4;
	cmos_gains.dgain_fine_shift = 0;
    
	cmos_gains.max_again = 16 << cmos_gains.again_shift;  //linear
	cmos_gains.max_dgain = 8 << cmos_gains.dgain_shift; //linear
	
    cmos_gains.isp_dgain_shift = 4;
    cmos_gains.isp_dgain = 1 << cmos_gains.isp_dgain_shift;
    cmos_gains.max_isp_dgain_target = 1 << cmos_gains.isp_dgain_shift;

	return &cmos_gains;
}

static __inline HI_U16 digital_gain_lut_get_value(HI_U8 index)
{
    static HI_U16 gain_lut[] = 
    {
        0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
        0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
        0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
        0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,0x8A,0x8B,0x8C
    };
	return gain_lut[index];
}




/*
 * This function applies the new gains to the ISP registers.
 */
static __inline HI_U16 cmos_gains_update(cmos_gains_const_ptr_t p_gains)
{

	HI_U16 data16;
	HI_U16 lut_val;

    /* analog gain 
      * analog gain = PGC * 0.3 db.
      * APGC = [0,80];
      * analog gain = [0db, 24db]. */
      
	{
        if(p_gains->again_db <= 80);
		{
            
		 sensor_write_register(PGC_ADDR, p_gains->again_db);
        }
	}

    /* digital gain. 
      * digital_gain = DPGC * 0.3 db.
      * DPCG  = [51h, 8Ch];
      * digital_gain = [0db, 18db]. */
      if(p_gains->again_db==80)
        {
     	  {
            if(p_gains->dgain_db <= 0x3C);
            {
                    lut_val = digital_gain_lut_get_value(p_gains->dgain_db);
            }
    		sensor_write_register(PGC_ADDR, lut_val);
    	   }
        }
	
	
}


static __inline HI_U32 analog_gain_from_exposure_calculate(
		cmos_gains_ptr_t p_gains,
		HI_U32 exposure,
		HI_U32 exposure_max,
		HI_U32 exposure_shift)
{
	int _i;
	HI_U32 _again = 0;
	HI_U32 exposure0, exposure1;
	int shft = 0;
	// normalize

	while (exposure > (1<<22))
	{
		exposure >>= 1;
		exposure_max >>= 1;
		++shft;
	}

    exposure0 = exposure;
       /* again unit: 0.3db */
	for(_i = 1; _i <= 80; _i++)
	{
		exposure1 = (exposure0*989) >> 10;
		if(exposure1 <= exposure_max)
			break;
		++_again;
		exposure0 = exposure1;
	}
	p_gains->again = (exposure << p_gains->again_shift) / exposure0; 
	p_gains->again_db = _again;
	return exposure0 << shft;
}

static __inline HI_U32 digital_gain_from_exposure_calculate(
		cmos_gains_ptr_t p_gains,
		HI_U32 exposure,
		HI_U32 exposure_max,
		HI_U32 exposure_shift)
{
	int _i;
	HI_U32 _dgain = 0;
	HI_U32 exposure0, exposure1;
	int shft = 0;
	// normalize
	while (exposure > (1<<20)) /* analog use (1<<22) for analog exposure is bigger. */
	{
		exposure >>= 1;
		exposure_max >>= 1;
		++shft;
	}

	exposure0 = exposure;

	/* unit: 0.3db */
	for(_i = 1; _i <= 0x3C; _i++)
	{
		exposure1 = (exposure0*989) >> 10;
		if(exposure1 <= exposure_max)
			break;
		++_dgain;
		exposure0 = exposure1;
	}
	p_gains->dgain = (exposure << p_gains->dgain_shift) / exposure0; 
       p_gains->dgain_db = _dgain; 	
	return exposure0 << shft;
}

static void setup_sensor(int isp_mode)
{
	if(0 == isp_mode) /* setup for ISP 'normal mode' */
	{
        sensor_write_register(VMAX_ADDR, 0x65);
        sensor_write_register(VMAX_ADDR + 1, 0x04);
	}
	else if(1 == isp_mode) /* setup for ISP pixel calibration mode */
	{
        //TODO: finish this.
        /* Sensor must be programmed for slow frame rate (5 fps and below)*/
        /* change frame rate to 3 fps by setting 1 frame length = 1125 * (30/3) */
        sensor_write_register(VMAX_ADDR, 0xF2);
        sensor_write_register(VMAX_ADDR + 1, 0x2B);

        /* Analog and Digital gains both must be programmed for their minimum values */
		sensor_write_register(PGC_ADDR, 0x00);
       // sensor_write_register(APGC_ADDR + 1, 0x00);
	//	sensor_write_register(DPGC_ADDR, 0x00);
	}
}

static __inline HI_U32 cmos_get_ISO(cmos_gains_ptr_t p_gains)
{
	HI_U32 _again;
	HI_U32 _dgain;

    _again = p_gains->again;
    _dgain = p_gains->dgain;
	p_gains->iso =  (((_again) * (_dgain) * 100) >> (4 + 4));

	return p_gains->iso;
}

/* Note: The unit of return value is 1db.  */
static HI_U8 cmos_get_analog_gain(cmos_gains_ptr_t cmos_gains)
{
    return (cmos_gains->again_db *  3 / 10); 
}

/* Note: The unit of return value is 1db.  */
static HI_U8 cmos_get_digital_gain(cmos_gains_ptr_t cmos_gains)
{
    return  (cmos_gains->dgain_db *  3 / 10); 
}

static HI_U8 cmos_get_digital_fine_gain(cmos_gains_ptr_t cmos_gains)
{
    return cmos_gains->dgain_fine;
}

static HI_U32 cmos_get_isp_default(cmos_isp_default_ptr_t p_coms_isp_default)
{
	if (NULL == p_coms_isp_default)
	{
	    printf("null pointer when get isp default value!\n");
	    return -1;
	}
    memcpy(p_coms_isp_default, &st_coms_isp_default, sizeof(cmos_isp_default_t));
    return 0;
}


HI_U32 cmos_get_isp_speical_alg(void)
{
    return isp_special_alg_awb;
}

static cmos_isp_demosaic_t st_isp_demosaic =
{
    /*vh_slope*/
    0xf5,

    /*aa_slope*/
    0x98,

    /*va_slope*/
    0xe6,

    /*uu_slope*/
    0x90,

    /*sat_slope*/
    0x5d,

    /*ac_slope*/
    0xcf,

    /*vh_thresh*/
    0x32,

    /*aa_thresh*/
    0x5b,

    /*va_thresh*/
    0x52,

    /*uu_thresh*/
    0x40,

    /*sat_thresh*/
    0x171,

    /*ac_thresh*/
    0x1b3,
};


static HI_U32 cmos_get_isp_demosaic(cmos_isp_demosaic_ptr_t p_cmos_isp_demosaic)
{
   if (NULL == p_cmos_isp_demosaic)
   {
	    printf("null pointer when get isp demosaic value!\n");
	    return -1;
   }
   memcpy(p_cmos_isp_demosaic, &st_isp_demosaic,sizeof(cmos_isp_demosaic_t));
   return 0;

}


static HI_U32 cmos_get_isp_agc_table(cmos_isp_agc_table_ptr_t p_cmos_isp_agc_table)
{
	if (NULL == p_cmos_isp_agc_table)
	{
	    printf("null pointer when get isp agc table value!\n");
	    return -1;
	}
    memcpy(p_cmos_isp_agc_table, &st_isp_agc_table, sizeof(cmos_isp_agc_table_t));
    return 0;
}


static HI_U32 cmos_get_isp_noise_table(cmos_isp_noise_table_ptr_t p_cmos_isp_noise_table)
{
	if (NULL == p_cmos_isp_noise_table)
	{
	    printf("null pointer when get isp noise table value!\n");
	    return -1;
	}
    memcpy(p_cmos_isp_noise_table, &st_isp_noise_table, sizeof(cmos_isp_noise_table_t));
    return 0;
}

/****************************************************************************
 * callback structure                                                       *
 ****************************************************************************/

static SENSOR_EXP_FUNC_S stSensorExpFuncs =
{
    .pfn_cmos_inttime_initialize = cmos_inttime_initialize,
    .pfn_cmos_inttime_update = cmos_inttime_update,

    .pfn_cmos_gains_initialize = cmos_gains_initialize,
    .pfn_cmos_gains_update = cmos_gains_update,
    .pfn_cmos_gains_update2 = NULL,
    .pfn_analog_gain_from_exposure_calculate = analog_gain_from_exposure_calculate,
    .pfn_digital_gain_from_exposure_calculate = digital_gain_from_exposure_calculate,

    .pfn_cmos_fps_set = cmos_fps_set,
    .pfn_vblanking_calculate = vblanking_calculate,
    .pfn_cmos_vblanking_front_update = cmos_vblanking_update,

    .pfn_setup_sensor = setup_sensor,

    .pfn_cmos_get_analog_gain = cmos_get_analog_gain,
    .pfn_cmos_get_digital_gain = cmos_get_digital_gain,
    .pfn_cmos_get_digital_fine_gain = NULL,
    .pfn_cmos_get_iso = cmos_get_ISO,

    .pfn_cmos_get_isp_default = cmos_get_isp_default,
    .pfn_cmos_get_isp_special_alg = NULL,
    .pfn_cmos_get_isp_agc_table = cmos_get_isp_agc_table,
	.pfn_cmos_get_isp_noise_table = cmos_get_isp_noise_table,
	.pfn_cmos_get_isp_demosaic = cmos_get_isp_demosaic,
	.pfn_cmos_get_isp_shading_table = NULL,

};

int sensor_register_callback(void)
{
	int ret;
	ret = HI_MPI_ISP_SensorRegCallBack(&stSensorExpFuncs);
	if (ret)
	{
	    printf("sensor register callback function failed!\n");
	    return ret;
	}

	return 0;
}

//chang sensor mode
int sensor_mode_set(HI_U8 u8Mode)
{
    switch(u8Mode)
    {
        //sensor mode 0
        case 0:
            gu8SensorMode = 0;
            // TODO:
        break;
        //sensor mode 1
        case 1:
            gu8SensorMode = 1;
             // TODO:
        break;

        default:
            printf("NOT support this mode!\n");
            return -1;
        break;
    }
    return 0;
}


#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */


#endif // __SONY_ICX692_H_
